---
title: Retrieve / Write lifecycle contract
summary: Define the read/write contract every concrete manager must implement.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-08-26
library: SAIF.Application
reference_type: embedded
operation_id: retrieve-write-lifecycle-contract
trigger: Front-end page load or save postback on a derived manager
---

# Retrieve / Write lifecycle contract

Defines the abstract read/write contract every concrete application manager must implement; this
library supplies no implementation, only the contract and the intended call sequence.

| Field | Value |
| --- | --- |
| **Operation ID** | retrieve-write-lifecycle-contract |
| **Trigger** | Front-end page load or save postback on a derived manager |
| **Inputs** | Derived-class context state |
| **Outputs** | `Boolean` success flag; `Message` text; persisted business objects |
| **Primary components** | `AppManagerBase` `MustOverride` members |
| **Entry point** | `SAIF.Application/AppManagerBase.vb` |

## Steps

1. **`SAIF.Application` defines the shape of every read/write operation but implements none of it.**
   `Retrieve()` and `Write()` exist on `AppManagerBase` purely as `MustOverride` (abstract) function
   signatures returning `Boolean`. This is a deliberate design choice: it forces every domain manager
   in every consuming library to explicitly decide what "load my data" and "save my data" mean for its
   own business object, while guaranteeing that front-end code calling `manager.Retrieve()` or
   `manager.Write()` works identically no matter which concrete manager it holds.
2. **The intended read sequence is a three-step guard chain.** A caller invoking `Retrieve()` is
   expected (by convention, not by enforced code in this library) to first confirm the manager's
   context is valid via the `ValidContext` check, then let the framework call `OnValidContext()` so the
   derived class can react to a newly-valid context, and only then call `OnRetrieve()` to actually pull
   data from the database. Skipping the validity check is possible because nothing in this library
   prevents it — the contract only describes the intended order, it does not enforce it.
3. **The intended write sequence brackets the actual save with an explicit transaction.** A derived
   manager is expected to call `OnTransactionStart()` before making any changes, perform the real save
   logic in `OnWrite()`, and finish by calling `OnTransactionStop(commit)` — passing `True` to commit
   the changes or `False` to roll them back. This bracketing exists specifically so that a save
   operation which fails partway through does not leave the database in a partially-updated state
   ([BR-APP-006](../business-rules.md#br-app-006-a-write-must-be-bracketed-by-an-explicit-transaction)).*
4. **The reference implementation lives outside this library, in `ClaimAppManager`
   (`SAIF.Application.Claim`)**, which shows how the contract is meant to be honored in practice: its
   `Retrieve()` explicitly checks `ValidContext` before doing any work, and its `Write()` wraps the call
   to `OnWrite()` in a `Try/Catch/Finally` block that guarantees `OnTransactionStop` is always called —
   with `True` on success and `False` if an exception was caught — so a database transaction is never
   left open regardless of outcome.
5. **Two related lifecycle hooks, `OnContextChanged()` and `OnCheckSecurity()`, are declared on the
   base class but never invoked by any code within `SAIF.Application` itself.** Responsibility for
   deciding *when* the business context has changed, and *when* security should be re-evaluated, is
   pushed entirely onto the derived manager — this library only defines the classification categories
   those checks would use
   ([BR-APP-003](../business-rules.md#br-app-003-access-classification-is-mutually-exclusive-and-read-only-externally)).

## Process flow

The following diagram visualizes the two parallel lifecycle paths this contract defines, and where
`SAIF.Application`'s responsibility (the abstract contract) hands off to the consuming manager's
responsibility (the concrete implementation, shown here via `ClaimAppManager`).

```mermaid
flowchart TD
	Trigger(["Page load or<br>save postback"]) --> Read["Read path triggered:<br>caller invokes Retrieve"]
	Trigger --> Write["Write path triggered:<br>caller invokes Write"]

	Read --> ValidCheck["Derived manager checks<br>ValidContext before<br>proceeding"]
	ValidCheck --> OnValid["OnValidContext reacts to<br>a newly-valid context"]
	OnValid --> OnRetrieve["OnRetrieve loads data<br>from the database"]
	OnRetrieve --> ReadDone(["Read complete"])

	Write --> TxStart["Derived manager calls<br>OnTransactionStart"]
	TxStart --> OnWrite["OnWrite performs the<br>actual save, wrapped in<br>Try/Catch by convention"]
	OnWrite --> Outcome{"Save succeeded?"}
	Outcome -->|Yes| Commit["OnTransactionStop(True)<br>commits the transaction"]
	Outcome -->|No, exception caught| Rollback["OnTransactionStop(False)<br>rolls back the transaction"]
	Commit --> WriteDone(["Write complete"])
	Rollback --> WriteDone
```

## Failure and edge-case behavior
- No implementation of `Retrieve`/`Write` exists in this library; behavior on failure is entirely
  determined by the derived class (e.g. `ClaimAppManager`'s `Try/Catch/Finally` wrapping of `OnWrite()`).

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
